﻿using System;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace LambdaRouting {
    public class LambdaRoute : RouteBase {
        private class LambdaControllerBase : ControllerBase {
            private readonly Func<RequestContext, ActionResult> action;

            public LambdaControllerBase(Func<RequestContext, ActionResult> action) {
                this.action = action;
            }

            protected override void ExecuteCore() {
                var result = action(ControllerContext.RequestContext);
                if (result != null)
                    result.ExecuteResult(ControllerContext);
            }
        }

        private class LambdaHttpHandler : IHttpHandler {
            private readonly LambdaControllerBase controller;
            private readonly RequestContext context;

            public LambdaHttpHandler(LambdaControllerBase controller, RequestContext context) {
                this.controller = controller;
                this.context = context;
            }

            public void ProcessRequest(HttpContext ctx) {
                (controller as IController).Execute(context);
            }

            public bool IsReusable {
                get { return false; }
            }
        }

        private class LambdaRouteHandler : IRouteHandler {
            private readonly LambdaControllerBase controller;

            public LambdaRouteHandler(LambdaControllerBase controller) {
                this.controller = controller;
            }

            public IHttpHandler GetHttpHandler(RequestContext requestContext) {
                return new LambdaHttpHandler(controller, requestContext);
            }
        }

        private readonly LambdaControllerBase controller;
        private readonly Regex url;

        private LambdaRoute(string url) {
            this.url = new Regex(url, RegexOptions.Compiled);
        }

        public LambdaRoute(string url, Action<RequestContext> f) : this(url) {
            controller = new LambdaControllerBase(x => {
                f(x);
                return null;
            });
        }

        public LambdaRoute(string url, Func<RequestContext, ActionResult> f): this(url) {
            controller = new LambdaControllerBase(f);
        }

        public LambdaRoute(string url, Func<ActionResult> f): this(url) {
            controller = new LambdaControllerBase(r => f());
        }

        public override RouteData GetRouteData(HttpContextBase httpContext) {
            if (!url.IsMatch(httpContext.Request.RawUrl))
                return null;
            var data = new RouteData(this, new LambdaRouteHandler(controller));
            data.Values["controller"] = url.ToString();
            return data;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
            return null;
        }
    }
}